#include "network_model_emesh_hop_by_hop.h"
#include "core.h"
#include "simulator.h"
#include "config.h"
#include "utils.h"
#include "packet_type.h"
#include "queue_model_history_list.h"
#include "memory_manager_base.h"
#include "dvfs_manager.h"
#include "stats.h"
#include "config.hpp"

#include <math.h>
#include <stdlib.h>

const char* output_direction_names[] = {
   "up", "down", "left", "right", "---", "self", "peer", "destination"
};
static_assert(NetworkModelEMeshHopByHop::MAX_OUTPUT_DIRECTIONS == sizeof(output_direction_names) / sizeof(output_direction_names[0]),
              "Not enough values in output_direction_names");

const char* OutputDirectionString(NetworkModelEMeshHopByHop::OutputDirection direction)
{
   LOG_ASSERT_ERROR(direction < NetworkModelEMeshHopByHop::MAX_OUTPUT_DIRECTIONS, "Invalid output direction %d", direction);
   return output_direction_names[direction];
}

NetworkModelEMeshHopByHop::NetworkModelEMeshHopByHop(Network* net, EStaticNetwork net_type):
   NetworkModel(net, net_type),
   m_enabled(false),
   m_total_bytes_sent(0),
   m_total_packets_sent(0),
   m_total_bytes_received(0),
   m_total_packets_received(0),
   m_total_contention_delay(SubsecondTime::Zero()),
   m_total_packet_latency(SubsecondTime::Zero()),
   m_fake_node(false),
   m_core_id(getNetwork()->getCore()->getId()),
   // Placeholders.  These values will be overwritten in a derived class.
//   m_link_bandwidth(Sim()->getDvfsManager()->getCoreDomain(m_core_id), 0),
   // m_hop_latency(Sim()->getDvfsManager()->getCoreDomain(m_core_id), 0),
   //新增
   m_intra_chiplet_bytes_sent(0),
   m_intra_chiplet_bytes_received(0),
   m_inter_chiplet_bytes_sent(0),
   m_inter_chiplet_bytes_received(0),
   m_intra_chiplet_bandwidth(Sim()->getDvfsManager()->getCoreDomain(m_core_id), 0), // 后续初始化片内带宽
   m_intra_chiplet_latency(Sim()->getDvfsManager()->getCoreDomain(m_core_id), 0),   // 后续初始化片内延迟
   m_inter_chiplet_bandwidth(Sim()->getDvfsManager()->getCoreDomain(m_core_id), 0), // 后续初始化片间带宽
   m_inter_chiplet_latency(Sim()->getDvfsManager()->getCoreDomain(m_core_id), 0),   // 后续初始化片间延迟
   m_chiplet_width(0),
   m_chiplet_height(0),
   m_num_chiplets_x(0),
   m_num_chiplets_y(0)
{
   // Get the Link Bandwidth, Hop Latency and if it has broadcast tree mechanism
//   try
//   {
//      // Link Bandwidth is specified in bits/clock_cycle
//      m_link_bandwidth = ComponentBandwidthPerCycle(Sim()->getDvfsManager()->getCoreDomain(m_core_id), Sim()->getCfg()->getInt("network/emesh_hop_by_hop/link_bandwidth"));
//      // Hop Latency is specified in cycles
//      m_hop_latency = ComponentLatency(Sim()->getDvfsManager()->getCoreDomain(m_core_id), Sim()->getCfg()->getInt("network/emesh_hop_by_hop/hop_latency"));
//
//      UInt32 smt_cores = Sim()->getCfg()->getInt("perf_model/core/logical_cpus");
//      m_concentration = Sim()->getCfg()->getInt("network/emesh_hop_by_hop/concentration") * smt_cores;
//      m_dimensions = Sim()->getCfg()->getInt("network/emesh_hop_by_hop/dimensions");
//      m_wrap_around = Sim()->getCfg()->getBool("network/emesh_hop_by_hop/wrap_around");
//
//      // Queue Model enabled? If no, this degrades into a hop counter model
//      m_queue_model_enabled = Sim()->getCfg()->getBool("network/emesh_hop_by_hop/queue_model/enabled");
//      m_queue_model_type = Sim()->getCfg()->getString("network/emesh_hop_by_hop/queue_model/type");
//
//      m_broadcast_tree_enabled = Sim()->getCfg()->getBool("network/emesh_hop_by_hop/broadcast_tree/enabled");
//   }
//
//   catch(...)
//   {
//      LOG_PRINT_ERROR("Could not read parameters from the configuration file");
//   }

    // zhangyan modify
    try
    {
        // 🌟 芯粒内通信 (片内)
        m_intra_chiplet_bandwidth = ComponentBandwidthPerCycle(
                Sim()->getDvfsManager()->getCoreDomain(m_core_id),
                Sim()->getCfg()->getInt("network/emesh_hop_counter/link_bandwidth")
        );

        m_intra_chiplet_latency = ComponentLatency(
                Sim()->getDvfsManager()->getCoreDomain(m_core_id),
                Sim()->getCfg()->getInt("network/emesh_hop_counter/hop_latency")
        );

        // 🌟 芯粒间通信 (片间)
        m_inter_chiplet_bandwidth = ComponentBandwidthPerCycle(
                Sim()->getDvfsManager()->getCoreDomain(m_core_id),
                Sim()->getCfg()->getInt("network/inter_chiplet/link_bandwidth")
        );

        m_inter_chiplet_latency = ComponentLatency(
                Sim()->getDvfsManager()->getCoreDomain(m_core_id),
                Sim()->getCfg()->getInt("network/inter_chiplet/hop_latency")
        );

//        // 其他保持原有
//        m_link_bandwidth = ComponentBandwidthPerCycle(
//                Sim()->getDvfsManager()->getCoreDomain(m_core_id),
//                Sim()->getCfg()->getInt("network/emesh_hop_by_hop/link_bandwidth")
//        );
//        m_hop_latency = ComponentLatency(
//                Sim()->getDvfsManager()->getCoreDomain(m_core_id),
//                Sim()->getCfg()->getInt("network/emesh_hop_by_hop/hop_latency")
//        );

        UInt32 smt_cores = Sim()->getCfg()->getInt("perf_model/core/logical_cpus");
        m_concentration = Sim()->getCfg()->getInt("network/emesh_hop_by_hop/concentration") * smt_cores;
        m_dimensions = Sim()->getCfg()->getInt("network/emesh_hop_by_hop/dimensions");
        m_wrap_around = Sim()->getCfg()->getBool("network/emesh_hop_by_hop/wrap_around");

        m_queue_model_enabled = Sim()->getCfg()->getBool("network/emesh_hop_by_hop/queue_model/enabled");
        m_queue_model_type = Sim()->getCfg()->getString("network/emesh_hop_by_hop/queue_model/type");

        m_broadcast_tree_enabled = Sim()->getCfg()->getBool("network/emesh_hop_by_hop/broadcast_tree/enabled");
    }
    catch(...)
    {
        LOG_PRINT_ERROR("Could not read parameters from the configuration file");
    }

   String name = String("network.")+EStaticNetworkStrings[net_type] + ".mesh";
   registerStatsMetric(name, m_core_id, "bytes-out", &m_total_bytes_sent);
   registerStatsMetric(name, m_core_id, "packets-out", &m_total_packets_sent);
   registerStatsMetric(name, m_core_id, "bytes-in", &m_total_bytes_received);
   registerStatsMetric(name, m_core_id, "packets-in", &m_total_packets_received);
   registerStatsMetric(name, m_core_id, "contention-delay", &m_total_contention_delay);
   registerStatsMetric(name, m_core_id, "total-delay", &m_total_packet_latency);


   //   新增
   String name_chiplet = String("network.") + "chiplet.mesh";
   registerStatsMetric(name_chiplet, m_core_id, "intra-chiplet-bytes-sent", &m_intra_chiplet_bytes_sent);
   registerStatsMetric(name_chiplet, m_core_id, "intra-chiplet-bytes-received", &m_intra_chiplet_bytes_received);
   registerStatsMetric(name_chiplet, m_core_id, "inter-chiplet-bytes-sent", &m_inter_chiplet_bytes_sent);
   registerStatsMetric(name_chiplet, m_core_id, "inter-chiplet-bytes-received", &m_inter_chiplet_bytes_received);

    computeMeshDimensions(m_mesh_width, m_mesh_height);

   if (m_core_id % m_concentration != 0 || m_core_id >= m_concentration * m_mesh_width * m_mesh_height)
   {
      m_fake_node = true;
      return;
   }

   createQueueModels(name);
}

NetworkModelEMeshHopByHop::~NetworkModelEMeshHopByHop()
{
   if (m_fake_node)
      return;

   for (UInt32 i = 0; i < NUM_OUTPUT_DIRECTIONS; i++)
   {
      if (m_queue_models[i])
         delete m_queue_models[i];
   }

   delete m_injection_port_queue_model;
   delete m_ejection_port_queue_model;
}

SInt32 NetworkModelEMeshHopByHop::computeChipletID(core_id_t core_id)
{
    SInt32 x, y;
    computePosition(core_id, x, y);

    SInt32 chiplet_width = Sim()->getCfg()->getInt("network/chiplet/chiplet_width");
    SInt32 chiplet_height = Sim()->getCfg()->getInt("network/chiplet/chiplet_height");
    SInt32 num_chiplets_x = Sim()->getCfg()->getInt("network/chiplet/num_chiplets_x");

    SInt32 chiplet_x = x / chiplet_width;
    SInt32 chiplet_y = y / chiplet_height;
    return chiplet_y * num_chiplets_x + chiplet_x;
}

//void
//NetworkModelEMeshHopByHop::createQueueModels(String name)
//{
//   SubsecondTime min_processing_time = m_link_bandwidth.getPeriod();
//
//   // Initialize the queue models for all the '4' output directions
//   m_queue_models[DOWN] = QueueModel::create(name+".link-down", m_core_id, m_queue_model_type, min_processing_time);
//   m_queue_models[LEFT] = QueueModel::create(name+".link-left", m_core_id, m_queue_model_type, min_processing_time);
//   m_queue_models[UP] = QueueModel::create(name+".link-up", m_core_id, m_queue_model_type, min_processing_time);
//   m_queue_models[RIGHT] = QueueModel::create(name+".link-right", m_core_id, m_queue_model_type, min_processing_time);
//
//   m_injection_port_queue_model = QueueModel::create(name+".link-in", m_core_id, m_queue_model_type, min_processing_time);
//   m_ejection_port_queue_model = QueueModel::create(name+".link-out", m_core_id, m_queue_model_type, min_processing_time);
//}

//zhangyan modify
void
NetworkModelEMeshHopByHop::createQueueModels(String name)
{
    // 预计算带宽对应的最小处理时间
    SubsecondTime min_processing_time_intra = m_intra_chiplet_bandwidth.getPeriod();
    SubsecondTime min_processing_time_inter = m_inter_chiplet_bandwidth.getPeriod();

    for (int dir = 0; dir < NUM_OUTPUT_DIRECTIONS; ++dir)
    {
        if (dir == LEFT || dir == RIGHT || dir == UP || dir == DOWN)
        {
            // 根据方向推算"下一跳core"，以判断是片内跳还是片间跳
            core_id_t neighbor_core_id = getNextDestCoreId((OutputDirection)dir);

            if (neighbor_core_id == INVALID_CORE_ID)
                continue; // 忽略非法方向，比如SELF/PEER/DESTINATION

            // 当前核和neighbor核，是否在同一个chiplet？
            SInt32 src_chiplet = computeChipletID(m_core_id);
            SInt32 dst_chiplet = computeChipletID(neighbor_core_id);

            SubsecondTime min_processing_time;
            if (src_chiplet == dst_chiplet)
                min_processing_time = min_processing_time_intra;
            else
                min_processing_time = min_processing_time_inter;

            m_queue_models[dir] = QueueModel::create(
                    name + ".link-" + OutputDirectionString((OutputDirection)dir),
                    m_core_id,
                    m_queue_model_type,
                    min_processing_time
            );
        }
    }

    // Injection和Ejection默认用片内带宽初始化（一般注入/弹出是本核内部）
    m_injection_port_queue_model = QueueModel::create(name+".link-in", m_core_id, m_queue_model_type, min_processing_time_intra);
    m_ejection_port_queue_model = QueueModel::create(name+".link-out", m_core_id, m_queue_model_type, min_processing_time_intra);
}


void
NetworkModelEMeshHopByHop::routePacket(const NetPacket &pkt, std::vector<Hop> &nextHops)
{
   ScopedLock sl(m_lock);

   core_id_t requester = INVALID_CORE_ID;

   if (pkt.type == SHARED_MEM_1)
      requester = getNetwork()->getCore()->getMemoryManager()->getShmemRequester(pkt.data);
   else // Other Packet types
      requester = pkt.sender;

   LOG_ASSERT_ERROR((requester >= 0) && (requester < (core_id_t) Config::getSingleton()->getTotalCores()),
         "requester(%i)", requester);

   UInt32 pkt_length = getNetwork()->getModeledLength(pkt);

   LOG_PRINT("pkt length(%u)", pkt_length);

//   if (pkt.sender == m_core_id)
//   {
//      m_total_packets_sent ++;
//      m_total_bytes_sent += pkt_length;
//   }

    if (pkt.sender == m_core_id)
    {
        m_total_packets_sent ++;
        m_total_bytes_sent += pkt_length;

        SInt32 src_chiplet = computeChipletID(pkt.sender);
        SInt32 dst_chiplet = computeChipletID(pkt.receiver);

        if (src_chiplet == dst_chiplet){
            m_intra_chiplet_bytes_sent += pkt_length;
            LOG_PRINT("[SEND] intra_chiplet_bytes_sent += %u (total now %lu)", pkt_length, m_intra_chiplet_bytes_sent);
        }
        else{
            m_inter_chiplet_bytes_sent += pkt_length;
            LOG_PRINT("[SEND] inter_chiplet_bytes_sent += %u (total now %lu)", pkt_length, m_inter_chiplet_bytes_sent);
        }
    }


   if (pkt.receiver == NetPacket::BROADCAST)
   {
      if (m_fake_node)
      {
         for (core_id_t i = 0; i < (core_id_t) Config::getSingleton()->getTotalCores(); i++)
         {
            addHop(DESTINATION, i, i, pkt.time, pkt_length, nextHops, requester);
         }
      }
      else if (m_broadcast_tree_enabled)
      {
         // Injection Port Modeling
         SubsecondTime injection_port_queue_delay = SubsecondTime::Zero();
         if (pkt.sender == m_core_id)
            injection_port_queue_delay = computeInjectionPortQueueDelay(pkt.receiver, pkt.time, pkt_length);
         SubsecondTime curr_time = pkt.time + injection_port_queue_delay;

         // Broadcast tree is enabled
         // Build the broadcast tree
         SInt32 sx, sy, cx, cy;

         computePosition(pkt.sender, sx, sy);
         computePosition(m_core_id, cx, cy);

         if (cy >= sy)
            addHop(UP, NetPacket::BROADCAST, computeCoreId(cx,cy+1), curr_time, pkt_length, nextHops, requester);
         if (cy <= sy)
            addHop(DOWN, NetPacket::BROADCAST, computeCoreId(cx,cy-1), curr_time, pkt_length, nextHops, requester);
         if (cy == sy)
         {
            if (cx >= sx)
               addHop(RIGHT, NetPacket::BROADCAST, computeCoreId(cx+1,cy), curr_time, pkt_length, nextHops, requester);
            if (cx <= sx)
               addHop(LEFT, NetPacket::BROADCAST, computeCoreId(cx-1,cy), curr_time, pkt_length, nextHops, requester);
            if (cx == sx)
               addHop(SELF, m_core_id, m_core_id, curr_time, pkt_length, nextHops, requester);
         }
      }
      else
      {
         // Broadcast tree is not enabled
         // Here, broadcast messages are sent as a collection of unicast messages
         LOG_ASSERT_ERROR(pkt.sender == m_core_id,
               "BROADCAST message to be sent at (%i), original sender(%i), Tree not enabled",
               m_core_id, pkt.sender);

         for (core_id_t i = 0; i < (core_id_t) Config::getSingleton()->getTotalCores(); i++)
         {
            // Injection Port Modeling
            SubsecondTime injection_port_queue_delay = computeInjectionPortQueueDelay(i, pkt.time, pkt_length);
            SubsecondTime curr_time = pkt.time + injection_port_queue_delay;

            // Unicast message to each core
            OutputDirection direction;
            core_id_t next_dest = getNextDest(i, direction);

            addHop(direction, i, next_dest, curr_time, pkt_length, nextHops, requester);
         }
      }
   }
   else if (m_fake_node)
   {
      addHop(DESTINATION, pkt.receiver, pkt.receiver, pkt.time, pkt_length, nextHops, requester);
   }
   else
   {
      // Injection Port Modeling
      SubsecondTime injection_port_queue_delay = SubsecondTime::Zero();
      if (pkt.sender == m_core_id)
      {
         injection_port_queue_delay = computeInjectionPortQueueDelay(pkt.receiver, pkt.time, pkt_length);
         *(subsecond_time_t*)&pkt.queue_delay += injection_port_queue_delay;
      }
      SubsecondTime curr_time = pkt.time + injection_port_queue_delay;

      // A Unicast packet
      OutputDirection direction;
      core_id_t next_dest = getNextDest(pkt.receiver, direction);

      addHop(direction, pkt.receiver, next_dest, curr_time, pkt_length, nextHops, requester, (subsecond_time_t*)&pkt.queue_delay);
   }
}

void
NetworkModelEMeshHopByHop::processReceivedPacket(NetPacket& pkt)
{
   ScopedLock sl(m_lock);

   UInt32 pkt_length = getNetwork()->getModeledLength(pkt);

   core_id_t requester = INVALID_CORE_ID;

   if (pkt.type == SHARED_MEM_1)
      requester = getNetwork()->getCore()->getMemoryManager()->getShmemRequester(pkt.data);
   else // Other Packet types
      requester = pkt.sender;

   LOG_ASSERT_ERROR((requester >= 0) && (requester < (core_id_t) Config::getSingleton()->getTotalCores()),
         "requester(%i)", requester);

   if ( (!m_enabled) || (requester >= (core_id_t) Config::getSingleton()->getApplicationCores())
                     || (m_core_id >= (core_id_t) Config::getSingleton()->getApplicationCores()))
      return;

//   SubsecondTime packet_latency = pkt.time - pkt.start_time;
//   SubsecondTime contention_delay = packet_latency - (computeDistance(pkt.sender, m_core_id) * m_hop_latency.getLatency());

//zhangyan modif
    SubsecondTime packet_latency = pkt.time - pkt.start_time;
    SubsecondTime expected_latency = SubsecondTime::Zero();
    SInt32 sx, sy, dx, dy;

    computePosition(pkt.sender, sx, sy);
    computePosition(m_core_id, dx, dy);

// 简单做 DOR（dimension-order routing）
// 先在 X 方向走
    while (sx != dx)
    {
        if (m_wrap_around && abs(sx - dx) > (m_mesh_width + 1) / 2)
        {
            if (sx < dx)
                sx = (sx - 1 + m_mesh_width) % m_mesh_width;
            else
                sx = (sx + 1) % m_mesh_width;
        }
        else
        {
            if (sx < dx)
                sx++;
            else
                sx--;
        }

        SInt32 src_chiplet = computeChipletID(pkt.sender);
        SInt32 dst_chiplet = computeChipletID(m_core_id);

        if (src_chiplet == dst_chiplet)
            expected_latency += m_intra_chiplet_latency.getLatency();
        else
            expected_latency += m_inter_chiplet_latency.getLatency();
    }

// 再在 Y 方向走
    while (sy != dy)
    {
        if (m_wrap_around && abs(sy - dy) > (m_mesh_height + 1) / 2)
        {
            if (sy < dy)
                sy = (sy - 1 + m_mesh_height) % m_mesh_height;
            else
                sy = (sy + 1) % m_mesh_height;
        }
        else
        {
            if (sy < dy)
                sy++;
            else
                sy--;
        }

        SInt32 src_chiplet = computeChipletID(pkt.sender);
        SInt32 dst_chiplet = computeChipletID(m_core_id);

        if (src_chiplet == dst_chiplet)
            expected_latency += m_intra_chiplet_latency.getLatency();
        else
            expected_latency += m_inter_chiplet_latency.getLatency();
    }

// 最后计算 contention delay
    SubsecondTime contention_delay = packet_latency - expected_latency;

    if (contention_delay < SubsecondTime::Zero())
        contention_delay = SubsecondTime::Zero();

    if (pkt.sender != m_core_id && !m_fake_node)
   {
      SubsecondTime processing_time = computeProcessingTime(pkt_length);
      SubsecondTime ejection_port_queue_delay = computeEjectionPortQueueDelay(pkt.time, pkt_length);

      packet_latency += (ejection_port_queue_delay + processing_time);
      contention_delay += ejection_port_queue_delay;
      pkt.time += (ejection_port_queue_delay + processing_time);
      pkt.queue_delay += ejection_port_queue_delay;
   }

    m_total_packets_received ++;
    m_total_bytes_received += pkt_length;

    SInt32 src_chiplet = computeChipletID(pkt.sender);
    SInt32 dst_chiplet = computeChipletID(m_core_id);

    if (src_chiplet == dst_chiplet){
        m_intra_chiplet_bytes_received += pkt_length;
        LOG_PRINT("[RECV] intra_chiplet_bytes_received += %u (total now %lu)", pkt_length, m_intra_chiplet_bytes_received);

    }
    else{
        m_inter_chiplet_bytes_received += pkt_length;
        LOG_PRINT("[RECV] inter_chiplet_bytes_received += %u (total now %lu)", pkt_length, m_inter_chiplet_bytes_received);

    }

    m_total_packet_latency += packet_latency;

   m_total_contention_delay += contention_delay;
}

void
NetworkModelEMeshHopByHop::addHop(OutputDirection direction,
      core_id_t final_dest, core_id_t next_dest,
      SubsecondTime pkt_time, UInt32 pkt_length,
      std::vector<Hop>& nextHops, core_id_t requester,
      subsecond_time_t *queue_delay_stats)
{
   Hop h;
   h.final_dest = final_dest;
   h.next_dest = next_dest;

   if (direction > NUM_OUTPUT_DIRECTIONS)
      h.time = pkt_time;
   else
      h.time = pkt_time + computeLatency(direction, pkt_time, pkt_length, requester, queue_delay_stats);

   nextHops.push_back(h);
}

SInt32
NetworkModelEMeshHopByHop::computeDistance(core_id_t sender, core_id_t receiver)
{
   SInt32 sx, sy, dx, dy;

   computePosition(sender, sx, sy);
   computePosition(receiver, dx, dy);

   if (m_wrap_around)
      return std::min(abs(sx - dx), m_mesh_width - abs(sx - dx))
           + std::min(abs(sy - dy), m_mesh_height - abs(sy - dy));
   else
      return abs(sx - dx) + abs(sy - dy);
}

void
NetworkModelEMeshHopByHop::computePosition(core_id_t core_id, SInt32 &x, SInt32 &y)
{
   x = (core_id / m_concentration) % m_mesh_width;
   y = (core_id / m_concentration) / m_mesh_width;
}

core_id_t
NetworkModelEMeshHopByHop::computeCoreId(SInt32 x, SInt32 y)
{
   x = (x + m_mesh_width) % m_mesh_width;
   y = (y + m_mesh_height) % m_mesh_height;
   return (y * m_mesh_width + x) * m_concentration;
}

//SubsecondTime
//NetworkModelEMeshHopByHop::computeLatency(OutputDirection direction, SubsecondTime pkt_time, UInt32 pkt_length, core_id_t requester, subsecond_time_t *queue_delay_stats)
//{
//   LOG_ASSERT_ERROR(!m_fake_node, "Cannot computeLatency on a fake network node");
//
//   LOG_ASSERT_ERROR((direction >= 0) && (direction < NUM_OUTPUT_DIRECTIONS),
//         "Invalid Direction(%u)", direction);
//
//   if ( (!m_enabled) || (requester >= (core_id_t) Config::getSingleton()->getApplicationCores()) )
//      return SubsecondTime::Zero();
//
//   SubsecondTime processing_time = computeProcessingTime(pkt_length);
//
//   SubsecondTime queue_delay = SubsecondTime::Zero();
//   if (m_queue_model_enabled)
//   {
//      queue_delay = m_queue_models[direction]->computeQueueDelay(pkt_time, processing_time);
//      if (queue_delay_stats)
//         *queue_delay_stats += queue_delay;
//   }
//
//   LOG_PRINT("Queue Delay(%s), Hop Latency(%s)", itostr(queue_delay).c_str(), itostr(m_hop_latency.getLatency()).c_str());
//   SubsecondTime packet_latency = m_hop_latency.getLatency() + queue_delay;
//
//   return packet_latency;
//}

// zhangyan modify
core_id_t NetworkModelEMeshHopByHop::getNextDestCoreId(OutputDirection direction)
{
    switch (direction)
    {
        case LEFT:  return computeCoreId((m_core_id / m_concentration) % m_mesh_width - 1, (m_core_id / m_concentration) / m_mesh_width);
        case RIGHT: return computeCoreId((m_core_id / m_concentration) % m_mesh_width + 1, (m_core_id / m_concentration) / m_mesh_width);
        case UP:    return computeCoreId((m_core_id / m_concentration) % m_mesh_width, (m_core_id / m_concentration) / m_mesh_width + 1);
        case DOWN:  return computeCoreId((m_core_id / m_concentration) % m_mesh_width, (m_core_id / m_concentration) / m_mesh_width - 1);
        case SELF:
        case DESTINATION:
        case PEER:
            return m_core_id;
        default:
        LOG_PRINT_ERROR("Unknown direction %d", direction);
            return INVALID_CORE_ID;
    }
}

SubsecondTime NetworkModelEMeshHopByHop::computeLatency(OutputDirection direction, SubsecondTime pkt_time, UInt32 pkt_length, core_id_t requester, subsecond_time_t *queue_delay_stats)
{
    LOG_ASSERT_ERROR(!m_fake_node, "Cannot computeLatency on a fake network node");

    if ((!m_enabled) || (requester >= (core_id_t) Config::getSingleton()->getApplicationCores()))
        return SubsecondTime::Zero();

    SubsecondTime processing_time = computeProcessingTime(pkt_length, direction);

    SubsecondTime queue_delay = SubsecondTime::Zero();
    if (m_queue_model_enabled)
    {
        queue_delay = m_queue_models[direction]->computeQueueDelay(pkt_time, processing_time);
        if (queue_delay_stats)
            *queue_delay_stats += queue_delay;
    }

    SubsecondTime hop_latency;

    SInt32 src_chiplet = computeChipletID(m_core_id);
    core_id_t next_core_id = getNextDestCoreId(direction);
    SInt32 dst_chiplet = computeChipletID(next_core_id);

    if (src_chiplet == dst_chiplet)
        hop_latency = m_intra_chiplet_latency.getLatency();
    else
        hop_latency = m_inter_chiplet_latency.getLatency();

    SubsecondTime packet_latency = hop_latency + queue_delay;

    return packet_latency;
}


SubsecondTime
NetworkModelEMeshHopByHop::computeInjectionPortQueueDelay(core_id_t pkt_receiver, SubsecondTime pkt_time, UInt32 pkt_length)
{
   LOG_ASSERT_ERROR(!m_fake_node, "Cannot computeInjectionPortQueueDelay on a fake network node");

   if (!m_queue_model_enabled)
      return SubsecondTime::Zero();

   if (pkt_receiver == m_core_id)
      return SubsecondTime::Zero();

   SubsecondTime processing_time = computeProcessingTime(pkt_length);
   return m_injection_port_queue_model->computeQueueDelay(pkt_time, processing_time);
}

SubsecondTime
NetworkModelEMeshHopByHop::computeEjectionPortQueueDelay(SubsecondTime pkt_time, UInt32 pkt_length)
{
   LOG_ASSERT_ERROR(!m_fake_node, "Cannot computeEjectionPortQueueDelay on a fake network node");

   if (!m_queue_model_enabled)
      return SubsecondTime::Zero();

   SubsecondTime processing_time = computeProcessingTime(pkt_length);
   return m_ejection_port_queue_model->computeQueueDelay(pkt_time, processing_time);
}

//SubsecondTime
//NetworkModelEMeshHopByHop::computeProcessingTime(UInt32 pkt_length)
//{
//   LOG_ASSERT_ERROR(!m_fake_node, "Cannot computeProcessingTime on a fake network node");
//
//   // Send: (pkt_length * 8) bits
//   // Bandwidth: (m_link_bandwidth) bits/cycle
//   UInt32 num_bits = pkt_length * 8;
//   return m_link_bandwidth.getRoundedLatency(num_bits);
//}

//zhangyan modify
SubsecondTime NetworkModelEMeshHopByHop::computeProcessingTime(UInt32 pkt_length, OutputDirection direction)
{
    LOG_ASSERT_ERROR(!m_fake_node, "Cannot computeProcessingTime on a fake network node");

    UInt32 num_bits = pkt_length * 8;
    SubsecondTime processing_time;

    SInt32 src_chiplet = computeChipletID(m_core_id);
    core_id_t next_core_id = getNextDestCoreId(direction);
    SInt32 dst_chiplet = computeChipletID(next_core_id);

    if (src_chiplet == dst_chiplet)
        processing_time = m_intra_chiplet_bandwidth.getRoundedLatency(num_bits);
    else
        processing_time = m_inter_chiplet_bandwidth.getRoundedLatency(num_bits);

    return processing_time;
}

SubsecondTime NetworkModelEMeshHopByHop::computeProcessingTime(UInt32 pkt_length)
{
    return computeProcessingTime(pkt_length, SELF);
}

SInt32
NetworkModelEMeshHopByHop::getNextDest(SInt32 final_dest, OutputDirection& direction)
{
   // Do dimension-order routing
   // Curently, do store-and-forward routing
   // FIXME: Should change this to wormhole routing eventually

   if (final_dest >= (core_id_t)Config::getSingleton()->getApplicationCores())
   {
      // We are a fake core (not an application core): warp straight to the destination
      direction = DESTINATION;
      return final_dest;
   }
   else if (m_core_id / m_concentration == final_dest / m_concentration)
   {
      // Destination is self, a peer on our concentrated node
      direction = DESTINATION;
      return final_dest;
   }
   else if (m_fake_node)
   {
      // We are a concentrated node but not the master: first send to master
      direction = PEER;
      return m_core_id - m_core_id % m_concentration;
   }

   SInt32 sx, sy, dx, dy;

   computePosition(m_core_id, sx, sy);
   computePosition(final_dest, dx, dy);

   if ((sx > dx) ^ (m_wrap_around && abs(sx - dx) > (m_mesh_width+1) / 2))
   {
      direction = LEFT;
      return computeCoreId(sx-1,sy);
   }
   else if (sx != dx)
   {
      direction = RIGHT;
      return computeCoreId(sx+1,sy);
   }
   else if ((sy > dy) ^ (m_wrap_around && abs(sy - dy) > (m_mesh_height+1) / 2))
   {
      direction = DOWN;
      return computeCoreId(sx,sy-1);
   }
   else if (sy != dy)
   {
      direction = UP;
      return computeCoreId(sx,sy+1);
   }
   else
   {
      // A send to itself
      direction = SELF;
      return m_core_id;
   }
}

void
NetworkModelEMeshHopByHop::enable()
{
   m_enabled = true;
}

void
NetworkModelEMeshHopByHop::disable()
{
   m_enabled = false;
}

bool
NetworkModelEMeshHopByHop::isEnabled()
{
   return m_enabled;
}

//void
//NetworkModelEMeshHopByHop::computeMeshDimensions(SInt32 &mesh_width, SInt32 &mesh_height)
//{
//   SInt32 core_count = Config::getSingleton()->getApplicationCores();
//   UInt32 smt_cores = Sim()->getCfg()->getInt("perf_model/core/logical_cpus");
//   SInt32 concentration = Sim()->getCfg()->getInt("network/emesh_hop_by_hop/concentration") * smt_cores;
//   SInt32 dimensions = Sim()->getCfg()->getInt("network/emesh_hop_by_hop/dimensions");
//   String size = Sim()->getCfg()->getString("network/emesh_hop_by_hop/size");
//
//   if (size == "")
//   {
//      switch(dimensions)
//      {
//         case 1: // line / ring
//            mesh_width = core_count / concentration;
//            mesh_height = 1;
//            break;
//         case 2: // 2-d mesh / torus
//            mesh_width = (SInt32) floor (sqrt(core_count / concentration));
//            mesh_height = (SInt32) ceil (1.0 * core_count / concentration / mesh_width);
//            break;
//         default:
//            LOG_PRINT_ERROR("Invalid value %d for dimensions, only 1 (line/ring) and 2 (mesh/torus) are currently supported", dimensions);
//      }
//
//      LOG_ASSERT_ERROR(core_count == (concentration * mesh_height * mesh_width), "Cannot build a mesh with %d cores (concentration %d), increase NumApplicationCores to %d for a %d x %d mesh or configure network/emesh_hop_by_hop/size=WIDTH:HEIGHT to manually specify mesh dimensions", core_count, concentration, concentration * mesh_width * mesh_height, mesh_width, mesh_height);
//   }
//   else
//   {
//      // ":"-separated, "," is for heterogeneity
//      int res = sscanf(size.c_str(), "%d:%d", &mesh_width, &mesh_height);
//      LOG_ASSERT_ERROR(res == 2, "Invalid mesh size \"%s\", expected \"width:height\"", size.c_str());
//
//      LOG_ASSERT_ERROR(core_count == (concentration * mesh_height * mesh_width), "Invalid mesh size %s for %d cores (concentration %d): %d x %d (x %d) == %d != %d", size.c_str(), core_count, concentration, mesh_width, mesh_height, concentration, concentration * mesh_width * mesh_height, core_count);
//   }
//}

//zhangyan modify
void
NetworkModelEMeshHopByHop::computeMeshDimensions(SInt32 &mesh_width, SInt32 &mesh_height)
{
    SInt32 total_cores = Sim()->getCfg()->getInt("network/chiplet/total_cores");  // 总核心数
    SInt32 cores_per_chiplet = Sim()->getCfg()->getInt("network/chiplet/cores_per_chiplet"); // 每个芯粒核心数
    SInt32 chiplet_width = Sim()->getCfg()->getInt("network/chiplet/chiplet_width");  // 芯粒内 Mesh 宽度
    SInt32 chiplet_height = Sim()->getCfg()->getInt("network/chiplet/chiplet_height"); // 芯粒内 Mesh 高度
    SInt32 num_chiplets_x = Sim()->getCfg()->getInt("network/chiplet/num_chiplets_x"); // 芯粒 X 方向个数
    SInt32 num_chiplets_y = Sim()->getCfg()->getInt("network/chiplet/num_chiplets_y"); // 芯粒 Y 方向个数

    // 计算芯粒内 Mesh 结构
    LOG_ASSERT_ERROR(cores_per_chiplet == chiplet_width * chiplet_height,
                     "Invalid chiplet size: cores_per_chiplet (%d) != chiplet_width * chiplet_height (%d * %d)",
                     cores_per_chiplet, chiplet_width, chiplet_height);

    // 计算芯粒间的 Mesh 结构
    LOG_ASSERT_ERROR(total_cores == cores_per_chiplet * num_chiplets_x * num_chiplets_y,
                     "Total cores (%d) != cores_per_chiplet * num_chiplets_x * num_chiplets_y (%d * %d * %d)",
                     total_cores, cores_per_chiplet, num_chiplets_x, num_chiplets_y);

    mesh_width = chiplet_width * num_chiplets_x;
    mesh_height = chiplet_height * num_chiplets_y;
}


std::pair<bool,SInt32>
NetworkModelEMeshHopByHop::computeCoreCountConstraints(SInt32 core_count)
{
   SInt32 mesh_width, mesh_height;
   computeMeshDimensions(mesh_width, mesh_height);

   assert(core_count <= mesh_width * mesh_height);
   assert(core_count > (mesh_width - 1) * mesh_height);
   assert(core_count > mesh_width * (mesh_height - 1));

   return std::make_pair(true,mesh_height * mesh_width);
}

std::pair<bool, std::vector<core_id_t> >
NetworkModelEMeshHopByHop::computeMemoryControllerPositions(SInt32 num_memory_controllers, SInt32 core_count)
{
   UInt32 smt_cores = Sim()->getCfg()->getInt("perf_model/core/logical_cpus");
   SInt32 concentration = Sim()->getCfg()->getInt("network/emesh_hop_by_hop/concentration") * smt_cores;
   SInt32 dimensions = Sim()->getCfg()->getInt("network/emesh_hop_by_hop/dimensions");
   SInt32 mesh_width, mesh_height;
   computeMeshDimensions(mesh_width, mesh_height);

   // core_id_list_along_perimeter : list of cores along the perimeter of the chip in clockwise order starting from (0,0)
   std::vector<core_id_t> core_id_list_along_perimeter;

   for (SInt32 i = 0; i < mesh_width; i++)
      core_id_list_along_perimeter.push_back(i);

   if (dimensions > 1 && mesh_height > 1)
   {
      for (SInt32 i = 1; i < (mesh_height-1); i++)
         core_id_list_along_perimeter.push_back((i * mesh_width) + mesh_width-1);

      for (SInt32 i = mesh_width-1; i >= 0; i--)
         core_id_list_along_perimeter.push_back(((mesh_height-1) * mesh_width) + i);

      for (SInt32 i = mesh_height-2; i >= 1; i--)
         core_id_list_along_perimeter.push_back(i * mesh_width);

      assert(core_id_list_along_perimeter.size() == (UInt32) (2 * (mesh_width + mesh_height - 2)));
   }

   LOG_ASSERT_ERROR(core_id_list_along_perimeter.size() >= (UInt32) num_memory_controllers,
         "num cores along perimeter(%u), num memory controllers(%i)",
         core_id_list_along_perimeter.size(), num_memory_controllers);

   SInt32 spacing_between_memory_controllers = core_id_list_along_perimeter.size() / num_memory_controllers;

   // core_id_list_with_memory_controllers : list of cores that have memory controllers attached to them
   std::vector<core_id_t> core_id_list_with_memory_controllers;

   for (SInt32 i = 0; i < num_memory_controllers; i++)
   {
      SInt32 index = (i * spacing_between_memory_controllers + mesh_width/2) % core_id_list_along_perimeter.size();
      core_id_list_with_memory_controllers.push_back(concentration * core_id_list_along_perimeter[index]);
   }

   return (std::make_pair(true, core_id_list_with_memory_controllers));
}
